package com.xbongbong.paas.script.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.script.pojo.dto.MigrateFixProductSpecificationDTO;
import com.xbongbong.paas.script.pojo.dto.ScriptBaseDTO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.BaseVO;
import com.xbongbong.paas.toolbox.wrap.XbbResponse;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.XbbProConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.domain.entity.SpecificationEntity;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.model.PaymentSheetModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.sys.model.CompanyConfigModel;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * 用于修复迁移产生的问题
 * @author 魏荣杰
 * @date 2019/11/14 4:12 下午
 * @since v4.6.0
 * @version v4.6.0
 */
@RestController
@RequestMapping(XbbProConstant.API_ROOT_PREFIX + "/migrateProblemFix")
public class MigrateProblemFixController {
    private static final Logger LOG = LoggerFactory.getLogger(MigrateProblemFixController.class);

    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PaymentSheetModel paymentSheetModel;


    @RequestMapping(value = "/fixProductSpecification", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixProductSpecification(@RequestBody @Valid MigrateFixProductSpecificationDTO migrateFixProductSpecificationDTO, BindingResult br) throws Exception {
        XbbResponse<BaseVO> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            List<Long> productIds = migrateFixProductSpecificationDTO.getProductIds();
            String corpid = migrateFixProductSpecificationDTO.getCorpid();
            Map<String, Object> param = new HashMap<>();
            param.put("corpid", corpid);
            param.put("productIdIn", productIds);
            param.put("del", 0);
            List<SpecificationEntity> specificationEntitys = specificationModel.findEntitys(param);
            Map<Long, List<SpecificationEntity>> productIdAndSpecificationMap = new HashMap<>();
            for (SpecificationEntity specificationEntity : specificationEntitys) {
                Long productId = specificationEntity.getProductId();
                List<SpecificationEntity> specificationEntityList;
                if (productIdAndSpecificationMap.containsKey(productId)) {
                    specificationEntityList = productIdAndSpecificationMap.get(productId);
                } else {
                    specificationEntityList = new ArrayList<>();
                }
                specificationEntityList.add(specificationEntity);
                productIdAndSpecificationMap.put(productId, specificationEntityList);
            }
            for (Map.Entry<Long, List<SpecificationEntity>> entry : productIdAndSpecificationMap.entrySet()) {
                Long productId = entry.getKey();
                List<SpecificationEntity> specificationEntities = entry.getValue();
                Map<String, List<SpecificationEntity>> nameAndSpecificationMap = new HashMap<>();
                for (SpecificationEntity entity : specificationEntities) {
                    String name = entity.getName();
                    List<SpecificationEntity> nameSpecificationEntities;
                    if (nameAndSpecificationMap.containsKey(name)) {
                        nameSpecificationEntities = nameAndSpecificationMap.get(name);
                    } else {
                        nameSpecificationEntities = new ArrayList<>();
                    }
                    nameSpecificationEntities.add(entity);
                    nameAndSpecificationMap.put(name, nameSpecificationEntities);
                }

                for (Map.Entry<String, List<SpecificationEntity>> entryValue : nameAndSpecificationMap.entrySet()) {
                    String name = entryValue.getKey();
                    List<SpecificationEntity> specificationEntityList = entryValue.getValue();
                    List<String> nameList = new ArrayList<>();
                    List<String> valueList = new ArrayList<>();
                    SpecificationEntity keepSpecification = new SpecificationEntity();
                    List<SpecificationEntity> deleteSpecifications = new ArrayList<>();
                    for (SpecificationEntity entity : specificationEntityList) {
                        String value = entity.getValue();
                        if (!nameList.contains(name)) {
                            keepSpecification = entity;
                            nameList.add(name);
                            valueList = JSONArray.parseArray(value, String.class);
                        } else {
                            entity.setDel(1);
                            deleteSpecifications.add(entity);
                            valueList.addAll(JSONArray.parseArray(value, String.class));
                        }
                    }
                    // 去重
                    LinkedHashSet<String> valueSet = new LinkedHashSet<>(valueList);
                    valueList = new ArrayList<>(valueSet);
                    keepSpecification.setValue(JSON.toJSONString(valueList));

                    // 更新数据
                    specificationModel.update(keepSpecification);
                    for (SpecificationEntity entity : deleteSpecifications) {
                        specificationModel.update(entity);
                    }
                }

                // 规格处理完毕，处理子产品数据
                BoolQueryBuilder filter = boolQuery()
                        .filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID), productId))
                        .filter(termQuery("del", 1))
                        .filter(termQuery("corpid.keyword", corpid));
                List<String> fieldList = new ArrayList<>();
                fieldList.add(FieldTypeEnum.DATAID.getAlias());
                List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT, filter, PaasFormDataEntityExt.class, fieldList);
                Set<Long> ids = new HashSet<>();
                for (PaasFormDataEntityExt esEntity : esEntities) {
                    ids.add(esEntity.getDataId());
                }

                param.clear();
                param.put("corpid", corpid);
                param.put("idIn", ids);
                List<ProductEntityExt> productEntities = productModel.findEntitys(param);
                for (ProductEntityExt entityExt : productEntities) {
                    entityExt.setDel(0);
                    entityExt.setCost(entityExt.getData().getDouble(ProductEnum.COST.getAttr()));
                    productModel.update(entityExt);
                }

                LOG.info("productId = " + productId + " fix specification finished");
            }

            LOG.info("all product specification fix finished");

            response = new XbbResponse<>(new BaseVO());
        }

        return JSON.toJSONString(response);
    }


//    @RequestMapping(value = "/fixProductExplainAndData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixProductExplainAndData(@RequestBody @Valid ScriptBaseDTO scriptBaseDTO, BindingResult br) throws Exception {
        XbbResponse<BaseVO> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            if (!Objects.equals(proBaseConfig.getXbbCode(), scriptBaseDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            String oneCorpid = scriptBaseDTO.getOneCorpid();
            if (StringUtil.isEmpty(oneCorpid)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }

            LOG.warn("fixProductExplainAndData start");

//            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.FIX_PRODUCT.getAlias(), oneCorpid);
//            if (!Objects.isNull(companyConfigEntity) && Objects.equals(companyConfigEntity.getConfigValue(), CompanyConfigEnum.FIX_PRODUCT.getValue())) {
//                LOG.warn("fixProductExplainAndData finish");
//                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
//            }

            PaasFormEntityExt paasFormEntity = paasFormModel.getByBusinessType(XbbRefTypeEnum.PRODUCT.getCode(), oneCorpid);
            if (Objects.isNull(paasFormEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018);
            }
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(paasFormEntity.getId(), oneCorpid);
            if (Objects.isNull(paasFormExplainEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018);
            }
            Map<String, String> attrMap = new HashMap<>();
            List<Integer> sortList = new ArrayList<>();
            List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                String attr = fieldAttrEntity.getAttr();
                String[] attrArray = attr.split("_");
                if (Objects.equals(attrArray[0], "array")) {
                    sortList.add(Integer.valueOf(attrArray[1]));
                }
            }
            sortList.add(0);
            sortList.sort(Integer::compareTo);

            for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                Integer fieldType = fieldAttrEntity.getFieldType();
                if (Objects.equals(fieldType, FieldTypeEnum.CHECKBOX_GROUP.getType()) || Objects.equals(fieldType, FieldTypeEnum.COMBO_CHECK.getType())) {
                    Integer sort = sortList.get(0) + 1;
                    while (sortList.contains(sort)) {
                        sort ++;
                    }
                    String attr = fieldAttrEntity.getAttr();
                    String newAttr = "array_" + sort;
                    attrMap.put(attr, newAttr);
                    fieldAttrEntity.setAttr(newAttr);
                    sortList.add(sort);
                    sortList.sort(Integer::compareTo);
                }
            }
            paasFormExplainEntity.setExplains(JSON.toJSONString(fieldAttrEntities));
            paasFormExplainModel.update(paasFormExplainEntity);

            Long id = 0L;
            Map<String, Object> params = new HashMap<>();
            params.put("corpid", oneCorpid);
            params.put("del", DelEnum.NORMAL.getDel());
            params.put("orderByStr", "id asc");
            params.put("pageSize", 100);
            while (true) {
                params.put("idGte", id);
                List<ProductEntityExt> productEntityExts = productModel.findEntitys(params);
                if (productEntityExts == null || productEntityExts.size() == 0) {
                    break;
                }
                id = productEntityExts.get(productEntityExts.size() - 1).getId();
                LOG.warn("fixProductExplainAndData 遍历id到" + id);
                for (ProductEntityExt productEntityExt : productEntityExts) {
                    JSONObject data = productEntityExt.getData();
                    for (Map.Entry<String, String> attr : attrMap.entrySet()) {
                        String key = attr.getKey();
                        String value = attr.getValue();
                        Object obj = data.get(key);
                        if (!Objects.isNull(obj)) {
                            data.put(value, obj);
                            data.put(key, null);
                        }
                    }
                }

                List<UpdateDataEntity> valueList = new ArrayList<>();
                productEntityExts.forEach((item)->{
                    valueList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), oneCorpid));
                });
                productModel.updateBatch(valueList, oneCorpid);

                for (ProductEntityExt productEntityExt : productEntityExts) {
                    JSONObject data = productEntityExt.getData();
                    Iterator<Map.Entry<String, Object>> iterator = data.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, Object> next = iterator.next();
                        Object value = next.getValue();
                        if (Objects.isNull(value)) {
                            iterator.remove();
                        }
                    }
                    productEntityExt.setCost(productEntityExt.getData().getDouble(ProductEnum.COST.getAttr()));
                    productModel.update(productEntityExt);
                }


            }
//            companyConfigModel.initConfigEntity(oneCorpid, CompanyConfigEnum.FIX_PRODUCT.getAlias());

            LOG.warn("fixProductExplainAndData finish");
            response = new XbbResponse<>(new BaseVO());
        }

        return JSON.toJSONString(response);
    }


    @RequestMapping(value = "/fixPaymentSheetRefIds", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixPaymentSheetRefIds(@RequestBody @Valid ScriptBaseDTO scriptBaseDTO, BindingResult br) throws Exception {
        XbbResponse<BaseVO> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            if (!Objects.equals(proBaseConfig.getXbbCode(), scriptBaseDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            String oneCorpid = scriptBaseDTO.getOneCorpid();
            if (StringUtil.isEmpty(oneCorpid)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }

            LOG.warn("fixPaymentSheetRefIds start");

            Long id = 0L;
            Map<String, Object> params = new HashMap<>();
            params.put("corpid", oneCorpid);
            params.put("del", DelEnum.NORMAL.getDel());
            params.put("orderByStr", "id asc");
            params.put("pageSize", 100);
            while (true) {
                params.put("idGte", id);
                List<PaymentSheetEntityExt> paymentSheetEntityExts = paymentSheetModel.findEntitys(params);
                if (paymentSheetEntityExts == null || paymentSheetEntityExts.size() == 0) {
                    break;
                }
                id = paymentSheetEntityExts.get(paymentSheetEntityExts.size() - 1).getId();
                LOG.warn("fixPaymentSheetRefIds 遍历id到" + id);
                for (PaymentSheetEntityExt entityExt : paymentSheetEntityExts) {
                    JSONObject data = entityExt.getData();
                    JSONArray contractIdArray = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
                    JSONArray paymentSheetIdArray = data.getJSONArray(PaymentSheetEnum.PAYMENT.getAttr());
                    if (!Objects.isNull(contractIdArray)) {
                        JSONArray newContractIdArray = new JSONArray();
                        for (Object object : contractIdArray) {
                            newContractIdArray.add(object.toString());
                        }
                        data.put(PaymentSheetEnum.CONTRACT.getAttr(), newContractIdArray);
                    }
                    if (!Objects.isNull(paymentSheetIdArray)) {
                        JSONArray newPaymentSheetIdArray = new JSONArray();
                        for (Object object : paymentSheetIdArray) {
                            newPaymentSheetIdArray.add(object.toString());
                        }
                        data.put(PaymentSheetEnum.PAYMENT.getAttr(), newPaymentSheetIdArray);
                    }

                    paymentSheetModel.update(entityExt);
                }
            }

            LOG.warn("fixPaymentSheetRefIds finish");
            response = new XbbResponse<>(new BaseVO());
        }
        return JSON.toJSONString(response);
    }
}
